<

コンテキスト メニューをカスタマイズする新しい方法

まとめ

コンテキスト メニュー、またはテキスト選択ツールバーは、長い場合に表示されるメニューです。 Flutter のテキストを押すか右クリックすると、次のようなオプションが表示されます。切るコピーペースト、 とすべて選択。以前は、 を使用してそれらを厳密にカスタマイズすることが可能ToolbarOptionsTextSelectionControls。現在は、ウィジェットを使用して構成可能になっています。 Flutter の他のすべてと同様、特定の設定パラメータには 廃止されました。

コンテクスト

以前は、次を使用してコンテキスト メニューのボタンを無効にすることができました。TextSelectionControlsただし、それを超えるカスタマイズにはコピーと フレームワーク内の数百行のカスタム クラスを編集します。さて、これらすべて 単純なビルダー関数に置き換えられました。contextMenuBuilder、 どれの 任意の Flutter ウィジェットをコンテキスト メニューとして使用できるようにします。

変更内容の説明

コンテキスト メニューは、contextMenuBuilderパラメータには、 すべてのテキスト編集ウィジェットとテキスト選択ウィジェットに追加されました。そうでない場合 提供されている場合、Flutter はそれを正しいコンテキストを構築するデフォルトに設定するだけです。 特定のプラットフォームのメニュー。これらのデフォルトのウィジェットはすべてユーザーに公開されます 再利用のために。コンテキスト メニューのカスタマイズは、次のようになります。contextMenuBuilder必要なウィジェットを返します。おそらく組み込みのウ​​ィジェットの再利用も含まれます。 コンテキスト メニュー ウィジェット。

を追加する方法を示す例は次のとおりです。メールを送るボタンをデフォルトに戻す 電子メール アドレスが選択されるたびにコンテキスト メニューが表示されます。完全なコードは見つかります サンプルリポジトリ内email_button_page.dartGitHub 上で。

TextField(
  contextMenuBuilder: (context, editableTextState) {
    final TextEditingValue value = editableTextState.textEditingValue;
    final List<ContextMenuButtonItem> buttonItems =
        editableTextState.contextMenuButtonItems;
    if (isValidEmail(value.selection.textInside(value.text))) {
      buttonItems.insert(
          0,
          ContextMenuButtonItem(
            label: 'Send email',
            onPressed: () {
              ContextMenuController.removeAny();
              Navigator.of(context).push(_showDialog(context));
            },
          ));
    }
    return AdaptiveTextSelectionToolbar.buttonItems(
      anchors: editableTextState.contextMenuAnchors,
      buttonItems: buttonItems,
    );
  },
)

さまざまなカスタム コンテキスト メニューの例が多数用意されています。サンプルリポジトリ内GitHub 上で。

関連するすべての非推奨機能には、非推奨の警告「使用してください」というフラグが付けられました。contextMenuBuilderその代わり。"

移行ガイド

一般に、コンテキスト メニューに対する以前の変更は現在非推奨になっています。 の使用が必要ですcontextMenuBuilder関連するパラメータ テキスト編集またはテキスト選択ウィジェット (の上TextField、 例えば)。次のような組み込みコンテキスト メニュー ウィジェットを返します。AdaptiveTextSelectionToolbarFlutter の組み込みコンテキスト メニューを使用するか、何かのために独自のウィジェットを返す 完全にカスタム。

移行するにはcontextMenuBuilder、次のパラメータとクラスには 廃止されました。

ToolbarOptions

このクラスは、以前は特定のボタンを明示的に有効または無効にするために使用されていました。 コンテキストメニューで。この変更の前に、それを次のように渡していた可能性があります。TextFieldまたは次のような他のウィジェット:

// Deprecated.
TextField(
  toolbarOptions: ToolbarOptions(
    copy: true,
  ),
)

ここで、buttonItemsに渡されましたAdaptiveTextSelectionToolbar。たとえば、次のことを保証できます。切るボタンは表示されませんが、他のボタンは通常どおり表示されます。

TextField(
  contextMenuBuilder: (context, editableTextState) {
    final List<ContextMenuButtonItem> buttonItems =
        editableTextState.contextMenuButtonItems;
    buttonItems.removeWhere((ContextMenuButtonItem buttonItem) {
      return buttonItem.type == ContextMenuButtonType.cut;
    });
    return AdaptiveTextSelectionToolbar.buttonItems(
      anchors: editableTextState.contextMenuAnchors,
      buttonItems: buttonItems,
    );
  },
)

または、切るボタンは常に排他的に表示されます。

TextField(
  contextMenuBuilder: (context, editableTextState) {
    return AdaptiveTextSelectionToolbar.buttonItems(
      anchors: editableTextState.contextMenuAnchors,
      buttonItems: <ContextMenuButtonItem>[
        ContextMenuButtonItem(
          onPressed: () {
            editableTextState.cutSelection(SelectionChangedCause.toolbar);
          },
          type: ContextMenuButtonType.cut,
        ),
      ],
    );
  },
)

TextSelectionControls.canCutおよびその他のボタンのブール値

これらのブール値は以前は、特定の機能を有効または無効にするのと同じ効果がありました。 ボタンとしてToolbarOptions.cut、などがありました。この変更が行われる前は、次のことが考えられます。 オーバーライドすることでボタンを非表示にしたり表示したりしていますTextSelectionControlsと これらのブール値を次のように設定します。

// Deprecated.
class _MyMaterialTextSelectionControls extends MaterialTextSelectionControls {
  @override
  bool canCut() => false,
}

前のセクションを参照してくださいToolbarOptions同様の効果を達成する方法については とcontextMenuBuilder

TextSelectionControls.handleCutおよびその他のボタンのコールバック

これらの関数により、ボタンが押されたときに呼び出されるコールバックの変更が可能になりました。 押し付けられました。この変更が行われる前は、コンテキスト メニューを変更していた可能性があります。 ボタンのコールバックは、次のようにこれらのハンドラー メソッドをオーバーライドして実行します。

// Deprecated.
class _MyMaterialTextSelectionControls extends MaterialTextSelectionControls {
  @override
  bool handleCut() {
    // My custom cut implementation here.
  },
}

これは引き続き使用できますcontextMenuBuilder、通話も含めて ツールバーを使用して、カスタム ハンドラーでの元のボタンのアクションを表示する のようなウィジェットAdaptiveTextSelectionToolbar.buttonItems

この例では、コピーダイアログを表示するボタン 通常のコピーロジックを実行します。

TextField(
  contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) {
    final List<ContextMenuButtonItem> buttonItems =
        editableTextState.contextMenuButtonItems;
    final int copyButtonIndex = buttonItems.indexWhere(
      (ContextMenuButtonItem buttonItem) {
        return buttonItem.type == ContextMenuButtonType.copy;
      },
    );
    if (copyButtonIndex >= 0) {
      final ContextMenuButtonItem copyButtonItem =
          buttonItems[copyButtonIndex];
      buttonItems[copyButtonIndex] = copyButtonItem.copyWith(
        onPressed: () {
          copyButtonItem.onPressed();
          Navigator.of(context).push(
            DialogRoute<void>(
              context: context,
              builder: (BuildContext context) =>
                const AlertDialog(
                  title: Text('Copied, but also showed this dialog.'),
                ),
            );
          )
        },
      );
    }
    return AdaptiveTextSelectionToolbar.buttonItems(
      anchors: editableTextState.contextMenuAnchors,
      buttonItems: buttonItems,
    );
  },
)

組み込みのコンテキスト メニュー アクションを変更する完全な例は、次の場所にあります。 サンプルリポジトリ修正済み_アクション_ページ.dartGitHub 上で。

buildToolbar

この関数は、次と同様のコンテキスト メニュー ウィジェットを生成しました。contextMenuBuilder, ただし、使用するにはさらにセットアップが必要でした。この変更が行われる前は、 上書きされていたかもしれないbuildToolbarの一部としてTextSelectionControls、 このような:

// Deprecated.
class _MyMaterialTextSelectionControls extends MaterialTextSelectionControls {
  @override
  Widget buildToolbar(
    BuildContext context,
    Rect globalEditableRegion,
    double textLineHeight,
    Offset selectionMidpoint,
    List<TextSelectionPoint> endpoints,
    TextSelectionDelegate delegate,
    ClipboardStatusNotifier clipboardStatus,
    Offset lastSecondaryTapDownPosition,
  ) {
    return _MyCustomToolbar();
  },
}

これで、簡単に使用できます13d33d13-184c-4598-90ff-8c5124398蜂パラメータとして直接TextField(その他)。パラメータで提供される情報は、buildToolbarから取得できますEditableTextStateそれが渡されるcontextMenuBuilder

次の例は、完全にカスタムのツールバーを最初から構築する方法を示しています。 デフォルトのボタンを使用したままです。

class _MyContextMenu extends StatelessWidget {
  const _MyContextMenu({
    required this.anchor,
    required this.children,
  });

  final Offset anchor;
  final List<Widget> children;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned(
          top: anchor.dy,
          left: anchor.dx,
          child: Container(
            width: 200,
            height: 200,
            color: Colors.amberAccent,
            child: Column(
              children: children,
            ),
          ),
        ),
      ],
    );
  }
}

class _MyTextField extends StatelessWidget {
  const _MyTextField();

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      maxLines: 4,
      minLines: 2,
      contextMenuBuilder: (context, editableTextState) {
        return _MyContextMenu(
          anchor: editableTextState.contextMenuAnchors.primaryAnchor,
          children: AdaptiveTextSelectionToolbar.getAdaptiveButtons(
            context,
            editableTextState.contextMenuButtonItems,
          ).toList(),
        );
      },
    );
  }
}

カスタム コンテキスト メニューを構築する完全な例は、サンプルにあります。 リポジトリのcustom_menu_page.dartGitHub 上で。

タイムライン

リリースされたバージョン: 3.6.0-0.0.pre
安定版リリース: 3.7.0

参考文献

API ドキュメント:

  • TextField.contextMenuBuilder
  • AdaptiveTextSelectionToolbar

関連する問題:

  • シンプルなカスタムテキスト選択ツールバー
  • テキストフィールドの外側の右クリックメニュー
  • デスクトップ用のテキスト編集 - 安定した
  • TextFields のコンテキスト メニューを無効にする機能
  • テキスト選択ツールバーのスタイル設定用の API がありません
  • すべてのウィジェットでコピー ツールバーを有効にする
  • ブラウザからのコンテキスト メニューを無効にする
  • カスタム コンテキスト メニューが Flutter Web に表示されない

関連する PR:

  • コンテキストメニュー
  • Web 上でブラウザのコンテキスト メニューを無効にする機能
  • Web (エンジン) 上でブラウザのコンテキスト メニューを無効にする機能
  • Web 上の SelectableRegion のカスタム コンテキスト メニュー
600bffbc-0290-4da0-8e10-512cc5345ベッド